using System;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Reflection;

using SBPweb.Contracts;
using SBPweb.Contracts.Implementation;

using SBPweb.Localization;

using SBPweb.Security.Authorization;

using SBPweb.Modules.Modularity;
using SBPweb.Modules.Logging;

using SBPweb.Presentation.Windows.Forms.Host.Controls;
using SBPweb.Presentation.Windows.Forms.Host.Services;

namespace SBPweb.Presentation.Windows.Forms.Host
{
    public static class Program
    {

        #region Internal properties

		internal static CultureInfo[] SupportedCultures
		{
			get
			{
				return sModuleLoader == null ? null : sModuleLoader.SupportedCultures.ToArray();
			}
		}

        #endregion

        #region Private fields

        private static ModuleLoader sModuleLoader;
        private static ResourceHandler sResourceHandler;
        private static MembershipUser sMembership;
		private static ILogManager sLogManager;

		private static string sModuleGroup;
        private static bool sIsInExplorerMode = false;

        #endregion

        #region Constructor logic / Entry point

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            if (Process.GetProcessesByName("explorer").Length == 0)
            {
                sIsInExplorerMode = true;
            }

			if (!sIsInExplorerMode)
			{
				string processName = Process.GetCurrentProcess().ProcessName;
				if (Process.GetProcessesByName(processName).Length > 1)
				{
					System.Windows.Forms.MessageBox.Show("You are not allowed to run two or more instances at same time!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
					Application.Exit();
					return;
				}
			}

            Console.WriteLine("Environment.CurrentDirectory : " + Environment.CurrentDirectory);
            Console.WriteLine("AppDomain.CurrentDomain.BaseDirectory : " + AppDomain.CurrentDomain.BaseDirectory);
            Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            sMembership = new MembershipUser();

			try
			{
				sLogManager = new LogManager();
				LogManager.Initialize();
			}
			catch (Exception ex)
			{
				System.Windows.Forms.MessageBox.Show(string.Format("Cannot initialize logger! Maybe you have not right to the log folder. Application halts.\n\n{0}", ex.ToString()), "Error", MessageBoxButtons.OK);
				Application.Exit();
				return;
			}

			LogManager.WriteVerbose(LogManager, string.Format("Process {0} starting.", System.Diagnostics.Process.GetCurrentProcess().ProcessName), "General");

			AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
			AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);

			Program.sModuleLoader = new ModuleLoader(Properties.Settings.Default.PathOfModules, Properties.Settings.Default.PatternOfModules, Properties.Settings.Default.ApplicationTitle, Application.CompanyName, LogManager);
			Program.sModuleLoader.ModuleLoading += new EventHandler<ModuleDescriptorEventArgs>(sModuleLoader_ModuleLoading);
            Program.sModuleLoader.ModuleLoaded += new EventHandler<ModuleEventArgs>(sModuleLoader_ModuleLoaded);
			Program.sModuleLoader.ModuleGroupLoaded += new EventHandler(sModuleLoader_ModuleGroupLoaded);
			Program.sResourceHandler = new ResourceHandler(typeof(MainForm), new List<CultureInfo>(SupportedCultures));

            Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
			AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

			StatePersistenceService sps = StatePersistenceService.GetInstance(sModuleLoader.ApplicationCompanyName, sModuleLoader.ApplicationTitle);
			sModuleLoader.Services.Add(typeof(IStatePersistenceService), sps);

			LoadPersistency();

			LogManager.WriteVerbose(LogManager, string.Format("Process {0} persistency loaded.", System.Diagnostics.Process.GetCurrentProcess().ProcessName), "General");

			MainForm mainForm = new MainForm();
            mainForm.SecurityPrincipal = sMembership;

			EasterEggHandlerService eehs = new EasterEggHandlerService(mainForm);
			sModuleLoader.Services.Add(typeof(IEasterEggHandlerService), eehs);

			sModuleLoader.Initialize(mainForm, sModuleGroup);

			LogManager.WriteVerbose(mainForm, string.Format("Process {0} modules loaded.", System.Diagnostics.Process.GetCurrentProcess().ProcessName), "General");

			Application.Run(mainForm);

			LogManager.WriteVerbose(mainForm, string.Format("Process {0} modules ending.", System.Diagnostics.Process.GetCurrentProcess().ProcessName), "General");

			SavePersistency();

			LogManager.WriteVerbose(mainForm, string.Format("Process {0} perisistency saved.", System.Diagnostics.Process.GetCurrentProcess().ProcessName), "General");

            sModuleLoader.Terminate();
        }

		static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
		{
			LogManager.WriteInfo(sender, string.Format("Assembly loaded: {0}", args.LoadedAssembly.FullName), "General");
		}

		static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
		{
			//This handler is called only when the common language runtime tries to bind to the assembly and fails.
			LogManager.WriteWarning(sender, string.Format("Assembly load failed: {0}", args.Name), "General");

			////Retrieve the list of referenced assemblies in an array of AssemblyName.
			//Assembly MyAssembly, objExecutingAssemblies;
			//string strTempAssmbPath = "";

			//objExecutingAssemblies = Assembly.GetExecutingAssembly();
			//AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();

			////Loop through the array of referenced assembly names.
			//foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
			//{
			//    //Check for the assembly names that have raised the "AssemblyResolve" event.
			//    if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
			//    {
			//        //Build the path of the assembly from where it has to be loaded.				
			//        strTempAssmbPath = "C:\\Myassemblies\\" + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
			//        break;
			//    }
			//}

			//if (!string.IsNullOrEmpty(strTempAssmbPath))
			//{
			//    //Load the assembly from the specified path. 					
			//    MyAssembly = Assembly.LoadFrom(strTempAssmbPath);

			//    LogManager.WriteInfo(sender, string.Format("Assembly explicitly loaded: {0}", args.Name), "General");
			//    //Return the loaded assembly.
			//    return MyAssembly;
			//}

			//LogManager.WriteError(sender, string.Format("Assembly cannot be loaded: {0}", args.Name), "General");
			return null;
		}

        #endregion

		#region Private methods

		#region Persistency

		private static void LoadPersistency()
		{
			IStatePersistenceService persistenceService = sModuleLoader.Services.Get<IStatePersistenceService>();
			if (persistenceService != null)
			{
				PersistencyDictionary state = persistenceService.Load(sModuleLoader);
				sModuleGroup = state.GetString("ModuleGroup", string.Empty);
			}
		}

		public static void SavePersistency()
		{
			IStatePersistenceService persistenceService = sModuleLoader.Services.Get<IStatePersistenceService>();
			if (persistenceService != null)
			{
				PersistencyDictionary state = persistenceService.Load(sModuleLoader);
				state.SetString("ModuleGroup", sModuleLoader.CurrentModuleGroupName);
				persistenceService.Save(sModuleLoader, state);
			}
		}

		#endregion

		#endregion

		#region Event handlers

		#region ModuleLoader functions

		static void sModuleLoader_ModuleGroupLoaded(object sender, EventArgs e)
        {
            Application.DoEvents();
        }

        static void sModuleLoader_ModuleLoading(object sender, ModuleDescriptorEventArgs e)
        {
			sModuleLoader.Host.LogManager.WriteInfo(sender, string.Format(LocalizationExtender.GetString("ModulLoading"), e.Descriptor.ModuleTypeName), "ModuleLoader");
        }

        static void sModuleLoader_ModuleLoaded(object sender, ModuleEventArgs e)
        {
			sModuleLoader.Host.LogManager.WriteInfo(sender, string.Format(LocalizationExtender.GetString("ModulLoaded"), e.Module, e.Module.Version), "ModuleLoader");
        }

        #endregion

        #region Error handling functions

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
		{
			sModuleLoader.Host.LogManager.WriteCritical(sender, e.ExceptionObject as Exception, SeverityLevel.Critical.ToString());

			Exception exc = (e.ExceptionObject as Exception);
			while (exc is System.Reflection.TargetInvocationException && exc.InnerException != null)
			{
				exc = exc.InnerException;
			}
			MainForm.ShowErrorForm(exc);
		}

		static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
		{
			sModuleLoader.Host.LogManager.WriteCritical(sender, e.Exception, SeverityLevel.Critical.ToString());

			Exception exc = e.Exception;

			while (exc is System.Reflection.TargetInvocationException && exc.InnerException != null)
			{
				exc = exc.InnerException;
			}

			MainForm.ShowErrorForm(exc);
        }

        #endregion

        #endregion

        #region Property accessors

        public static bool IsInExplorerMode
        {
            get
            {
                return sIsInExplorerMode;
            }
        }

        public static ModuleLoader Loader
        {
            get
            {
                return sModuleLoader;
            }
        }

        public static ResourceHandler Resources
        {
            get
            {
                return sResourceHandler;
            }
        }

		public static ILogManager LogManager
		{
			get
			{
				return sLogManager;
			}
		}

        #endregion

    }
}
